using System.Collections.Generic;

using FineCollection.Intf;


namespace FineCollection.Const
{
    
    /// <summary>
    /// Immutable set. 
    /// All items are different, nulls are not allowed.
    /// </summary>
    /// <typeparam name="T">type of an item.</typeparam>
    public class ConstTinySet<T> : ConstArray<T>, RSet<T>
    {
        private readonly IEqualityComparer<T> equator;


        #region CONSTRUCTORS

        /// <summary>
        /// Makes an immutable set with 2 items.
        /// </summary>
        /// <param name="item1">the first item.</param>
        /// <param name="item2">the second item.</param>
        /// <remarks>
        /// Given items must be different and not null.
        /// </remarks>
        public ConstTinySet(T item1, T item2)
            : base(1, 2)
        {
            equator = EqualityComparer<T>.Default;

            if (equator.Equals(item1,item2))
                throw new DuplicatesFoundException();
            arr[0] = item1;
            arr[1] = item2;
        }


        /// <summary>
        /// Makes an immutable set with 3 items.
        /// </summary>
        /// <param name="item1">the first item.</param>
        /// <param name="item2">the second item.</param>
        /// <param name="item3">the third item.</param>
        /// <remarks>
        /// Given items must be different and not null.
        /// </remarks>
        public ConstTinySet(T item1, T item2, T item3) 
            : base(1, 3)
        {
            equator = EqualityComparer<T>.Default;

            // TODO check that all items are different ???
            arr[0] = item1;
            arr[1] = item2;
            arr[2] = item3;
        }


        /// <summary>
        /// Makes an immutable set with 4 items.
        /// </summary>
        /// <param name="item1">the first item.</param>
        /// <param name="item2">the second item.</param>
        /// <param name="item3">the third item.</param>
        /// <param name="item4">the fourth item.</param>
        /// <remarks>
        /// Given items must be different and not null.
        /// </remarks>
        public ConstTinySet(T item1, T item2, T item3, T item4) 
            : base(1, 4)
        {
            equator = EqualityComparer<T>.Default;

            // TODO check that all items are different ???
            arr[0] = item1;
            arr[1] = item2;
            arr[2] = item3;
            arr[3] = item4;
        }


        /// <summary>
        /// Makes an immutable set copying items from the specified array.
        /// </summary>
        /// <param name="items">array with items to make a set.</param>
        /// <remarks>
        /// All items in the given array must be different and with no nulls.
        /// </remarks>
        public ConstTinySet(T[] items)
            : base(1, items, items.Length, false)
        {
            equator = EqualityComparer<T>.Default;

            // TODO check that all items are different ???
        }


        /// <summary>
        /// Makes an immutable set copying items from the specified array.
        /// </summary>
        /// <param name="items">array with items to make a set.</param>
        /// <param name="equator">custom equator.</param>
        /// <remarks>
        /// All items in the given array must be different and with no nulls.
        /// </remarks>
        public ConstTinySet(T[] items, IEqualityComparer<T> equator)
            : base(1, items, items.Length, false)
        {
            this.equator = equator;

            // TODO check that all items are different ???
        }


        /// <summary>
        /// This constructor is for internal usage only.
        /// </summary>
        /// <param name="array">array with items.</param>
        /// <param name="count">count of real itema in the array.</param>
        /// <param name="takeOwnership">true - use this array as is, 
        ///                             false - copy its content to a new array.</param>
        /// <param name="equator">custom equator.</param>
        private ConstTinySet(T[] array, int count, bool takeOwnership, IEqualityComparer<T> equator) 
            : base(1, array, count, takeOwnership)
        {
            this.equator = equator;
        }

        #endregion



        /// <summary>
        /// Checks whether the specified item presents in this collection.
        /// </summary>
        /// <param name="item">items to check for presence.</param>
        /// <remarks>
        /// The complexity of this function is <i>o(n)</i>.
        /// </remarks>
        public bool Contains(T item)
        {
            for (int i = 0; i < count; i++)
                if (arr[i].Equals(item))
                    return true;
            return false;
        }



        public static RSet<T> operator + (ConstTinySet<T> s1, ConstTinySet<T> s2)
        {
            bool b1 = s1 != null && s1.IsNotEmpty,
                 b2 = s2 != null && s2.IsNotEmpty;

            if (b1 && b2)
            {
                if (s1.count <= s2.count)
                    return Merge(s1, s2);
                else
                    return Merge(s2, s1);
            }
            else if (b1)
            {
                return s1;
            }
            else if (b2)
            {
                return s2;
            }
            else
            {
                return ConstZero<T>.zero;
            }
        }


        private static ConstTinySet<T> Merge(ConstTinySet<T> s1, ConstTinySet<T> s2)
        {
            int n1 = s1.count,
                n2 = s2.count;

            int limit = n1 + n2,
                n3 = n1;

            T[] arr3 = new T[limit];
            System.Array.Copy(s1.arr, arr3, n1);

            for (int i = 0; i < n2; i++)
            {
                bool unique = true;
                for (int j = 0; j < n1; j++)
                    if (s1.equator.Equals(s2.arr[i], s1.arr[j])) { unique = false; break; }
                if (unique)
                    arr3[n3++] = s2.arr[i];
            }

            if (n3 == n1)
                return s1;
            else
                return new ConstTinySet<T>(arr3, n3, true, s1.equator);
        }
    }


}